/*
 *     *********************************************************************
 *     * Copyright (C) 1988, 1990 Stanford University.                     *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  Stanford University                 *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

/*
 * The routines in this file handle binary network files input/output.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "defs.h"
#include "net.h"
#include "globals.h"
#include "bin_io.h"
#include "net_macros.h"


private FILE	*fnet;			/* file for reading/writing network */
private tptr	rd_tlist;		/* list of transistor just read */
private double	inet_CTGA;		/* transistor capacitances */
private double	inet_CTDE;
private double	inet_CTDW;
private long	inet_lambda;
private void   adjust_transistors( tptr tlist );
private const char * const netHeader = "<<inet>>";
private const char * const endAscii  = "<end>\n";

#define	CM_M	100.0			/* centimicrons per micron */
#define	CM_M2	( CM_M * CM_M )		/* centimicrons per micron ^ 2 */


#define	HEADER_LEN		(sizeof( netHeader ) - 1)

/* define number of bytes used to write/read network parameter */
#define	NB_NDINDEX	4
#define	NB_NCAP		4
#define	NB_TSIZE	4
#define	NB_COORD	4
#define	NB_VTHRESH	2
#define	NB_TP		2
#define	NB_STRLEN	2
#define	NB_FLAGS	1
#define	NB_TTYPE	1

/* node flags we are interested in preserving */
#define	SAVE_FLAGS	(POWER_RAIL | ALIAS | USERDELAY)

/* scale factors for floating-point numbers */
#define	VTH_SCALE		1000.0
#define	CAP_SCALE		16384.0    /* power of 2 to minimize error */


typedef struct {	/* format of node records in net file */
   union {
      char  cap[ NB_NCAP ];
      char  alias[ NB_NDINDEX ];
   } n;
   char  flags[ NB_FLAGS ];
   char  slen[ NB_STRLEN ];
} File_Node;

#define	Size_File_Node		( NB_NDINDEX + NB_FLAGS + NB_STRLEN )


typedef struct {	/* format of node user-delays in net file */
   char  tphl[ NB_TP ];
   char  tplh[ NB_TP ];
} File_Delay;

#define	Size_File_Delay		( NB_TP + NB_TP )


typedef struct {	/* format of node thresholds in net file */
   char  vhigh[ NB_VTHRESH ];
   char  vlow[ NB_VTHRESH ];
} File_Thresh;

#define	Size_File_Thresh 	( NB_VTHRESH + NB_VTHRESH )


typedef struct {	/* format of transistor records in net file */
   char  width[ NB_TSIZE ];
   char  length[ NB_TSIZE ];
   char  ttype[ NB_TTYPE ];
   char  gate[ NB_NDINDEX ];
   char  src[ NB_NDINDEX ];
   char  drn[ NB_NDINDEX ];
   char  x[ NB_COORD ];
   char  y[ NB_COORD ];
} File_Trans;

#define	Size_File_Trans	( NB_TSIZE + NB_TSIZE + NB_TTYPE + \
		NB_NDINDEX + NB_NDINDEX + NB_NDINDEX + NB_COORD + NB_COORD )


private tptr wr_nodes() {
   register nptr  n;
   register tptr  txlist;
   register lptr  l;
   File_Node      node;
   File_Delay     delays;
   File_Thresh    thresh;
   int            slen;
   double         cap;
   float          vhigh = HIGHTHRESH, vlow = LOWTHRESH;

   txlist = NULL;

   for( n = GetNodeList(); n != NULL; n = n->n.next ) {
      cap = n->ncap;
      for( l = n->nterm; l != NULL; l = l->next ) {
         register tptr  t = l->xtor;

         if( not ( t->tflags & ACTIVE_T ) ) {
            t->scache.t = txlist;
            txlist = t;
            t->tflags |= ACTIVE_T;
         }
         if( t->ttype & GATELIST )
            cap += ( StackCap( t ) / 2.0 );
      }

      slen = strlen( pnode( n ) ) + 1;
      PackBytes( node.slen, slen, NB_STRLEN );
      node.flags[0] = n->nflags & SAVE_FLAGS;
      if( n->vhigh != vhigh or n->vlow != vlow )
         node.flags[0] |= WATCHED;
      if( n->nflags & ALIAS )
         PackBytes( node.n.alias, Node2index( n->nlink ), NB_NDINDEX )
         else
            PackBytes( node.n.cap, cap * CAP_SCALE, NB_NCAP );
      ( void ) Fwrite( (char*)&node, Size_File_Node, fnet );
      ( void ) Fwrite( pnode( n ), slen, fnet );

      if( n->nflags & USERDELAY ) {
         PackBytes( delays.tphl, n->tphl, NB_TP );
         PackBytes( delays.tplh, n->tplh, NB_TP );
         ( void ) Fwrite( (char*)&delays, Size_File_Delay, fnet );
      }
      if( node.flags[0] & WATCHED ) {
         PackBytes( thresh.vhigh, n->vhigh * VTH_SCALE, NB_VTHRESH );
         PackBytes( thresh.vlow, n->vlow * VTH_SCALE, NB_VTHRESH );
         ( void ) Fwrite( (char*)&thresh, Size_File_Thresh, fnet );
      }
   }

   return( txlist );
}


private void wr_trans( tptr  t ) {
   File_Trans  trans;
   Ulong       ndindx;

   if( t->ttype & ORED ) {
      for( t = t->tlink; t != NULL; t = t->scache.t ) wr_trans( t );
      return;
   }

   ndindx = Node2index( t->gate );
   PackBytes( trans.gate, ndindx, NB_NDINDEX );

   ndindx = Node2index( t->source );
   PackBytes( trans.src, ndindx, NB_NDINDEX );

   ndindx = Node2index( t->drain );
   PackBytes( trans.drn, ndindx, NB_NDINDEX );

   PackBytes( trans.length, t->r->length, NB_TSIZE );
   PackBytes( trans.width, t->r->width, NB_TSIZE );

   trans.ttype[0] = t->ttype & ~( ORED | ORLIST | STACKED | GATELIST );

   if( t->tlink != t ) {
      trans.ttype[0] |= GATELIST;
      PackBytes( trans.x, t->x.pos, NB_COORD );
      PackBytes( trans.y, t->y.pos, NB_COORD );
   }

   ( void ) Fwrite( (char*)&trans, Size_File_Trans, fnet );
}


private void wr_txtors( tptr tlist ) {
   register tptr  t, next;

   for( t = tlist; t != NULL; t = next ) {
      next = t->scache.t;

      t->tflags &= ~ACTIVE_T;
      t->scache.t = NULL;

      if( t->ttype & GATELIST ) {
         for( t = ( tptr ) t->gate; t != NULL; t = t->scache.t )
            wr_trans( t );
      } else
         wr_trans( t );
   }

   for( t = tcap->dcache.t; t != tcap; t = t->dcache.t )
      wr_trans( t );
}


private void WriteAscii( FILE  *f ) {
   ( void ) fprintf( f, "lambda %ld\n", LAMBDACM );
   ( void ) fprintf( f, "CTGA %.6f\n", CTGA * CM_M2 );
   ( void ) fprintf( f, "CTDW %.6f\n", CTDW * CM_M );
   ( void ) fprintf( f, "CTDE %.6f\n", CTDE );

   /* add extensions here */
   ( void ) fprintf( f, endAscii );
}


private int ReadAscii( FILE * f, char * line ) {
   
   int  nl = 0;

   inet_CTGA = CTGA;
   inet_CTDE = CTDE;
   inet_CTDW = CTDW;
   inet_lambda = LAMBDACM;

   do {
      if( fgetline( line, 200, f ) == NULL )
         return( -1 );
      nl++;
      if( strcmp( line, endAscii ) == 0 )
         return( 0 );
      if( strncmp( line, "lambda ", 7 ) == 0 )
         inet_lambda = atoi( line + 7 );
      if( strncmp( line, "CTGA ", 5 ) == 0 )
         inet_CTGA = atof( line + 5 ) / CM_M2;
      else if( strncmp( line, "CTDW ", 5 ) == 0 )
         inet_CTDW = atof( line + 5 ) / CM_M;
      else if( strncmp( line, "CTDE ", 5 ) == 0 )
         inet_CTDE = atof( line + 5 );

      /* add extensions here */
   } while( nl < 30 );

   ( void ) fprintf( stderr, "inet file seems bad\n" );
   return( -1 );
}


public void wr_netfile( char * fname ) {
   tptr  txlist;

   if( ( fnet = fopen( fname, "w" ) ) == NULL ) {
      ( void ) fprintf( stderr, "can't open file '%s'\n", fname );
      return;
   }

   ( void ) fprintf( fnet, "%s\n", netHeader );
   ( void ) fprintf( fnet, "%d %d\n", GetHashSize(), nnodes + naliases );

   WriteAscii( fnet );
   txlist = wr_nodes();
   wr_txtors( txlist );
   ( void ) fclose( fnet );
}


private void PrematureEof() {
   ( void ) fprintf( stderr, "premature eof in inet file\n" );
   exit( 1 );
}


private void rd_nodes( char * nname, int n_nodes ) {
   File_Node    node;
   File_Delay   delays;
   File_Thresh  thresh;
   Ulong        tmp;
   int          slen;
   nptr         n;
   nptr         aliases;

   aliases = NULL;
   while( n_nodes-- != 0 ) {
      if( Fread( (char*)&node, Size_File_Node, fnet ) != Size_File_Node ) {
         PrematureEof();
      }

      UnpackBytes( node.slen, slen, NB_STRLEN );
      if( Fread( nname, slen, fnet ) != slen ) {
         PrematureEof();
      }

      n = GetNode( nname );

      n->nflags = node.flags[0];
      if( n->nflags & ALIAS ) {
         UnpackBytes( node.n.alias, tmp, NB_NDINDEX );
         n->c.time = tmp;

         n->n.next = aliases;
         aliases = n;
      } else {
         UnpackBytes( node.n.cap, tmp, NB_NCAP );
         n->ncap = tmp * ( 1/CAP_SCALE );
         if( n->ncap < MIN_CAP )
            n->ncap = MIN_CAP;
      }

      if( n->nflags & USERDELAY ) {
         if( Fread( (char*)&delays, Size_File_Delay, fnet ) != Size_File_Delay ) {
            PrematureEof();
         }
         UnpackBytes( delays.tphl, n->tphl, NB_TP );
         UnpackBytes( delays.tplh, n->tplh, NB_TP );
      }
      if( n->nflags & WATCHED ) {
         n->nflags &= ~WATCHED;
         if( Fread( (char*)&thresh, Size_File_Thresh, fnet ) != Size_File_Thresh ) {
            PrematureEof();
         }
         UnpackBytes( thresh.vhigh, tmp, NB_VTHRESH );
         n->vhigh = tmp * ( 1/VTH_SCALE );
         UnpackBytes( thresh.vlow, tmp, NB_VTHRESH );
         n->vlow = tmp * ( 1/VTH_SCALE );
      }
   }

   VDD_node->nflags |= INPUT;		/* these bits get thrashed above */
   GND_node->nflags |= INPUT;

   for( n = aliases; n != NULL; n = n->n.next ) {
      n->nlink = Index2node( n->c.time );
      n->c.time = 0;
      nnodes--;
      naliases++;
   }
}


private void rd_txtors() {
   File_Trans  trans;
   Ulong       ndindx;
   long        width, length;
   tptr        t, *last;

   rd_tlist = NULL;
   last = &rd_tlist;
   while( Fread( (char*)&trans, Size_File_Trans, fnet ) == Size_File_Trans ) {
      NEW_TRANS( t );

      UnpackBytes( trans.gate, ndindx, NB_NDINDEX );
      t->gate = Index2node( ndindx );

      UnpackBytes( trans.src, ndindx, NB_NDINDEX );
      t->source = Index2node( ndindx );

      UnpackBytes( trans.drn, ndindx, NB_NDINDEX );
      t->drain = Index2node( ndindx );

      UnpackBytes( trans.width, width, NB_TSIZE );
      UnpackBytes( trans.length, length, NB_TSIZE );

      t->ttype = trans.ttype[0];

      if( t->ttype & GATELIST ) {
         t->ttype &= ~GATELIST;
         UnpackBytes( trans.x, t->x.pos, NB_COORD );
         UnpackBytes( trans.y, t->y.pos, NB_COORD );
         EnterPos( t, TRUE );
      } else
         EnterPos( t, FALSE );

      t->r = requiv( ( int ) t->ttype, width, length );

      ntrans[ BASETYPE( t->ttype ) ] += 1;

      *last = t;
      last = &( t->scache.t );
   }
   *last = NULL;
}


public int rd_netfile( FILE * f, char * line ) {
   int   hash_size, n_nodes;
   char  *s;

   if( strncmp( line, netHeader, HEADER_LEN ) != 0 )
      return( FALSE );

   if( fgetline( line, 200, f ) == NULL )
      PrematureEof();

   if( sscanf( line, "%d %d", &hash_size, &n_nodes ) != 2 ) {
      ( void ) fprintf( stderr, "bad format for net file\n" );
      exit( 1 );
   }

   if( hash_size != GetHashSize() ) {
      ( void ) fprintf( stderr, "Incompatible net file version\n" );
      exit( 1 );
   }

   if( ReadAscii( f, line ) ) PrematureEof();

   fnet = f;
   rd_nodes( line, n_nodes );
   rd_txtors();

   adjust_transistors( rd_tlist );

   return( TRUE );
}


private void adjust_transistors( tptr tlist ) {
   double         adj_g, adj_w, adj_e;
   register tptr  t;

#    define	ABS( X )	( ((X) < 0) ? -(X) : (X) )
#    ifndef CAP_ADJUST_LIMIT
#	define	CAP_ADJUST_LIMIT	0.0000005
#    endif
#    define	G_CAP_ADJUST_LIMIT	(CAP_ADJUST_LIMIT / CM_M2)
#    define	W_CAP_ADJUST_LIMIT	(CAP_ADJUST_LIMIT / CM_M)

   adj_g = CTGA - inet_CTGA;
   adj_w = CTDW - inet_CTDW;
   adj_e = CTDE - inet_CTDE;

   if( LAMBDACM != inet_lambda and LAMBDACM > 0 and inet_lambda > 0 ) {
      double  adj_size = ( double ) LAMBDACM / ( double ) inet_lambda;

      for( t = tlist; t != NULL; t = t->scache.t ) {
         t->r->length *= adj_size;
         t->r->width *= adj_size;
      }
   }

   if( ABS( adj_g ) > G_CAP_ADJUST_LIMIT ) {
      for( t = tlist; t != NULL; t = t->scache.t )
         t->gate->ncap += adj_g * t->r->length * t->r->width;
   }

   if( ABS( adj_w ) > W_CAP_ADJUST_LIMIT or ABS( adj_e ) > CAP_ADJUST_LIMIT ) {
      for( t = tlist; t != NULL; t = t->scache.t ) {
         t->source->ncap += adj_w * t->r->width + adj_e;
         t->drain->ncap += adj_w * t->r->width + adj_e;
      }
   }
}


/*
 * Traverse the transistor list and add the node connection-list.  We have
 * to be careful with ALIASed nodes.  Note that transistors with source/drain
 * connected to VDD and GND nodes are not linked.
 */
public nptr bin_connect_txtors() {
   register tptr  t, tnext;
   register nptr  nd_list = NULL;

   for( t = rd_tlist; t != NULL; t = tnext ) {
      tnext = t->scache.t;
      t->state = ( t->ttype & ALWAYSON ) ? WEAK : UNKNOWN;
      t->tflags = 0;

      if( t->ttype & TCAP ) {
         LINK_TCAP( t );
      } else {
         if( t->ttype & ALWAYSON ) {
            CONNECT( on_trans, t );
         } else {
            CONNECT( t->gate->ngate, t );
         }

         if( not ( t->source->nflags & POWER_RAIL ) ) {
            CONNECT( t->source->nterm, t );
            LINK_TO_LIST( t->source, nd_list, VISITED );
         }
         if( not ( t->drain->nflags & POWER_RAIL ) ) {
            CONNECT( t->drain->nterm, t );
            LINK_TO_LIST( t->drain, nd_list, VISITED );
         }
      }
   }
   return( nd_list );
}
